package com.coincalf.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.coincalf.dto.*;
import com.coincalf.entity.*;
import com.coincalf.framework.constants.Constant;
import com.coincalf.framework.dto.ForexMarketDTO;
import com.coincalf.framework.enums.*;
import com.coincalf.framework.exception.AccountException;
import com.coincalf.framework.exception.ForexAccountException;
import com.coincalf.framework.exception.GlobalDefaultException;
import com.coincalf.framework.http.Response;
import com.coincalf.framework.utils.DateUtil;
import com.coincalf.mapper.EntrustOrderMapper;
import com.coincalf.service.*;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.ExecutionException;

/**
 * <p>
 * 委托订单信息 服务实现类
 * </p>
 *
 * @author qiang
 * @since 2018-05-12
 */
@Service
@Slf4j
public class EntrustOrderServiceImpl extends ServiceImpl<EntrustOrderMapper, EntrustOrder> implements EntrustOrderService, Constant {

    @Autowired
    private ConfigService configService;
    @Autowired
    private MarketService marketService;
    @Autowired
    private AccountService accountService;
    @Autowired
    private ForexOpenPositionOrderService forexOpenPositionOrderService;
    @Autowired
    private ForexAccountService forexAccountService;
    @Autowired
    private TradeAreaService tradeAreaService;
    @Autowired
    private TurnoverOrderService turnoverOrderService;
    @Autowired
    private LockService lockService;

    /**
     * 币币交易委托下单
     *
     * @param market 交易对
     * @param order  委托订单
     * @param userId 用户ID
     */
    @Override
    @Transactional
    public Response createEntrustOrder(Market market, CreateTradeOrderDTO order, long userId) throws GlobalDefaultException {
        DecimalFormat format = new DecimalFormat("#.########");
        // 交易开关配置
        Config config = configService.queryByTypeAndCode(Constant.CONFIG_TYPE_SYSTEM, Constant.CONFIG_TRADE_STATUS);
        if (config == null || StringUtils.isEmpty(config.getValue()) || config.getValue().equals("0")) {
            log.error("交易服务暂停");
            throw new GlobalDefaultException(50001);
        }
        if (!DateUtil.isTradeTime(market.getTradeTime(), market.getTradeWeek())) {
            log.error("不在交易时间范围内");
            throw new GlobalDefaultException(50028);
        }
        String[] coinNames = market.getName().split("[/]");
        // 预计成交额 = 用户委托数量 * 用户限定价格
        BigDecimal amount = order.getPrice().multiply(order.getVolume()).setScale(SCALE, BigDecimal.ROUND_HALF_UP);
        // 委托数量校验
        if (market.getNumMin().compareTo(BigDecimal.ZERO) > 0 && market.getNumMin().compareTo(order.getVolume()) > 0) {
            log.error("单笔最小委托量: " + market.getNumMin() + "，当前委托量: " + order.getVolume());
            // throw new GlobalDefaultException(50003);
            return Response.err(50003, "单笔最小委托量: " + format.format(market.getNumMin()) + coinNames[0]);
        }
        // 委托数量校验
        if (market.getNumMax().compareTo(BigDecimal.ZERO) > 0 && market.getNumMax().compareTo(order.getVolume()) < 0) {
            log.error("单笔最大委托量: " + market.getNumMax() + "，当前委托量: " + order.getVolume());
            throw new GlobalDefaultException(50004);
            //return Response.err(50004,"单笔最大委托量: " + format.format(market.getNumMax()) + coinNames[0]);
        }
        // 成交额校验
        if (market.getTradeMin().compareTo(BigDecimal.ZERO) > 0 && market.getTradeMin().compareTo(amount) > 0) {
            log.error("单笔最小委托交易额: " + market.getTradeMin() + "，当前委托预计成交额: " + amount);
            // throw new GlobalDefaultException(50005);
            return Response.err(50005, "单笔最小委托交易额: " + format.format(market.getTradeMin()) + coinNames[1]);
        }
        // 成交额校验
        if (market.getTradeMax().compareTo(BigDecimal.ZERO) > 0 && market.getTradeMax().compareTo(amount) < 0) {
            log.error("单笔最大委托交易额: " + market.getTradeMax() + "，当前委托预计成交额: " + amount);
            // throw new GlobalDefaultException(50006);
            return Response.err(50006, "单笔最大委托交易额: " + format.format(market.getTradeMax()) + coinNames[1]);
        }
        // 交易手续费比例
        BigDecimal feeRate = order.getType() == OrderType.BUY.getCode() ? market.getFeeBuy() : market.getFeeSell();
        // 交易费用 = 订单金额 * 交易手续费
        BigDecimal fee = amount.multiply(feeRate).setScale(SCALE, BigDecimal.ROUND_HALF_UP);
        // 冻结资金（包含手续费)
        BigDecimal freezeAmount = order.getType() == OrderType.BUY.getCode() ? amount.add(fee) : order.getVolume();
        // 创建委托订单
        EntrustOrder entrustOrder = new EntrustOrder();
        entrustOrder.setMarketId(market.getId())    // 交易对ID
                .setUserId(userId)                  // 用户ID
                .setMarketName(market.getName())    // 交易对名称
                .setSymbol(order.getSymbol())       // 交易对标识符
                .setMarketType(market.getType())    // 交易对类型
                .setPrice(order.getPrice())         // 价格
                .setType(order.getType())           // 买卖类型
                .setMergeLowPrice(this.getMergePrice(order.getPrice(), market.getMergeDepth(), DepthMergeType.LOW))     // 合并价格
                .setMergeHighPrice(this.getMergePrice(order.getPrice(), market.getMergeDepth(), DepthMergeType.HIGH))   // 合并价格
                .setVolume(order.getVolume())       // 委托量
                .setAmount(amount)                  // 预计成交额
                .setFeeRate(feeRate)                // 手续费费率
                .setFee(fee)                        // 手续费
                .setFreeze(freezeAmount)            // 冻结资金
                .setDeal(BigDecimal.ZERO)           // 已成交量
                .setTradeType(1)                    // 交易类型 1开仓 2平仓
                .setPriceType(order.getPriceType()) //价格类型 1市价 2限价
                .setStatus(OrderStatus.PENDING.getCode()) // 状态
                .setCreated(new Date())
                .setLastUpdateTime(new Date());
        baseMapper.insert(entrustOrder);
        // 订单号
        long orderId = entrustOrder.getId();
        try {
            if (order.getType() == OrderType.BUY.getCode()) {
                accountService.lockAmount(userId, market.getBuyCoinId(), freezeAmount, BusinessType.TRADE_CREATE, orderId);
            } else {
                // 委托卖单冻结
                accountService.lockAmount(userId, market.getSellCoinId(), freezeAmount, BusinessType.TRADE_CREATE, orderId);
            }
        } catch (AccountException e) {
            log.error("委托下单失败：{}", e.getMessage());
            throw new GlobalDefaultException(50027);
        }
        return Response.ok(orderId);
    }

    /**
     * 币币交易撤销委托
     *
     * @param orderId 订单号
     * @param userId  用户ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Market cancelEntrustOrder(long orderId, long userId) throws GlobalDefaultException {
        if (orderId <= 0L) {
            log.error("撤单失败，订单号错误，orderId：{}", orderId);
            throw new GlobalDefaultException(50008);
        }
        boolean isLocked = false;
        try {
            // 加锁，避免重复提交撤单请求导致重复解冻资金出现资金异常
            isLocked = lockService.getLock(REDIS_KEY_TRADE_ORDER_CANCEL_LOCK, String.valueOf(orderId), false);
            if (!isLocked) {
                log.error("获取撤单锁失败");
                throw new GlobalDefaultException(50010);
            }
            EntrustOrder order = baseMapper.selectById(orderId);
            if (order == null) {
                log.error("撤单失败，委托订单不存在，orderId：{}", orderId);
                throw new GlobalDefaultException(50008);
            }
            if (order.getUserId() != userId) {
                log.error("当前登录用户与委托订单所属用户不一致，订单所属用户：{}，当前登录用户：{}", orderId, userId);
                throw new GlobalDefaultException(50009);
            }
            if (order.getStatus() != OrderStatus.PENDING.getCode()) {
                log.error("撤单失败，orderId：{}, status：{}", orderId, order.getStatus());
                throw new GlobalDefaultException(50010);
            }
            Market market = marketService.selectById(order.getMarketId());
            int count = baseMapper.modifyTradeEntrustOrder(order.getId(), BigDecimal.ZERO, BigDecimal.ZERO, OrderStatus.CANCEL.getCode());
            if (count == 1) {
                // 解冻资金
                BigDecimal unlockAmount = order.getFreeze();
                long coinId = order.getType() == OrderType.BUY.getCode() ? market.getBuyCoinId() : market.getSellCoinId();
                try {
                    accountService.unlockAmount(order.getUserId(), coinId, unlockAmount, BusinessType.TRADE_CREATE, orderId);
                    return market;
                } catch (AccountException e) {
                    log.error("撤单失败，解冻资金账户失败，orderId：{}", orderId);
                    throw new GlobalDefaultException(50019);
                }
            }
        } catch (GlobalDefaultException e) {
            log.error(e.getMessage());
            e.printStackTrace();
            throw new GlobalDefaultException(50010);
        } finally {
            if (isLocked) {
                lockService.unlock(REDIS_KEY_TRADE_ORDER_CANCEL_LOCK, String.valueOf(orderId));
            }
        }
        log.error("撤单失败，解冻资金账户失败，orderId：{}", orderId);
        throw new GlobalDefaultException(50010);
    }

    /**
     * 创新交易委托下单
     *
     * @param market 交易对
     * @param order  委托订单
     * @param userId 用户ID
     * @return
     */
    @Override
    @Transactional
    public Response createForexOrder(Market market, CreateForexOrderDTO order, long userId) {
        DecimalFormat format = new DecimalFormat("#.########");
        // 交易开关配置
        Config config = configService.queryByTypeAndCode(CONFIG_TYPE_SYSTEM, CONFIG_FOREX_STATUS);
        if (config == null || StringUtils.isEmpty(config.getValue()) || config.getValue().equals("0")) {
            log.error("交易服务暂停");
            throw new GlobalDefaultException(50001);
        }
        if (!DateUtil.isTradeTime(market.getTradeTime(), market.getTradeWeek())) {
            log.error("不在交易时间范围内");
            throw new GlobalDefaultException(50028);
        }
        // 委托数量校验
        if (market.getNumMin().compareTo(BigDecimal.ZERO) == 1
                && market.getNumMin().compareTo(order.getVolume()) == 1) {
            log.error("单笔最小委托量: " + market.getNumMin() + "，当前委托量: " + order.getVolume());
            //throw new GlobalDefaultException(50003);
            return Response.err(50003, "单笔最小委托量: " + format.format(market.getNumMin()));
        }
        // 委托数量校验
        if (market.getNumMax().compareTo(BigDecimal.ZERO) == 1
                && market.getNumMax().compareTo(order.getVolume()) == -1) {
            log.error("单笔最大委托量: " + market.getNumMax() + "，当前委托量: " + order.getVolume());
            //throw new GlobalDefaultException(50004);
            return Response.err(50004, "单笔最大委托量: " + format.format(market.getNumMax()));
        }
        // 委托数量交易
        if (order.getOrderId() > 0) {
            // 查询持仓订单
            ForexOpenPositionOrder openPositionOrder = forexOpenPositionOrderService.selectById(order.getOrderId());
            if (openPositionOrder == null) {
                log.error("平仓订单不存在, openPositionId: {}", order.getOrderId());
                throw new GlobalDefaultException(50015);
            }
            if (openPositionOrder.getStatus() == OpenPositionOrderStatus.CLOSE.getCode()) {
                log.error("此持仓订单已平仓, openPositionId: {}", order.getOrderId());
                throw new GlobalDefaultException(50016);
            }
            if (order.getType() != openPositionOrder.getType()) {
                BigDecimal canClose = openPositionOrder.getNum().subtract(openPositionOrder.getCloseNum());
                if (canClose.compareTo(order.getVolume()) == -1) {
                    log.error("平仓委托量不能超过持仓量, openPositionId: {}", order.getOrderId());
                    throw new GlobalDefaultException(50017);
                }
            }
        }
        // 合约单位
        BigDecimal contractUnit = new BigDecimal(market.getContractUnit());
        // 保证金比例
        BigDecimal marginRate = market.getMarginRate();
        // 基础货币对（USDT/BTC）兑换率
        BigDecimal baseCoinRate = marketService.getBaseToCoinRate(market);
        // 报价货币对（BTC/USDT）兑换率
        BigDecimal priceCoinRate = marketService.getPriceToCoinRate(market);
        // 保证金 = 合约单位 * 基础货币对（USDT/BTC）兑汇率 * 保证金比例 * 委托量
        BigDecimal margin = contractUnit                // 合约单位
                .multiply(baseCoinRate)                 // * 基础货币对报价货币兑换率
                .multiply(marginRate)                   // * 保证金比例
                .multiply(order.getVolume())            // * 委托量
                .setScale(SCALE, BigDecimal.ROUND_UP);
        // 委托价格
        BigDecimal price = order.getPrice();
        // 交易手续费费率
        BigDecimal feeRate = order.getType() == OrderType.BUY.getCode() ? market.getFeeBuy() : market.getFeeSell();
        // 交易手续费费用 = 委托量 * 委托价 * 合约单位 * 报价货币对（BTC/USDT）兑换率 * 交易手续费费率
        BigDecimal orderFee = order.getVolume().multiply(price)
                .multiply(contractUnit)
                .multiply(priceCoinRate)
                .multiply(feeRate)
                .setScale(SCALE, BigDecimal.ROUND_HALF_UP);
        // 判断是否需要平仓（持有与当前委托单相反方向的仓位）
        OrderType orderType = OrderType.BUY;
        if (order.getType() == OrderType.BUY.getCode()) {
            orderType = OrderType.SELL;
        }
        // 查询和当前委托持仓方向相反的持仓账户，用来判断当前委托单是开仓还是平仓
        ForexAccount forexAccount = forexAccountService.queryByUserAndMarketAndType(market.getId(), userId, orderType);
        if (forexAccount == null) {
            log.error("资金账户尚未初始化");
            throw new GlobalDefaultException(50018);
        }
        // 委托单类型
        TradeType tradeType = TradeType.OPEN_POSITION;
        // 已经有持仓
        if (forexAccount.getAmount().compareTo(BigDecimal.ZERO) > 0) {
            tradeType = TradeType.CLOSE_POSITION;
            // 平仓委托不占用保证金
            margin = BigDecimal.ZERO;
            // 如果持仓量小于当前委托量，则不允下单平仓
            if (forexAccount.getAmount().compareTo(order.getVolume()) < 0) {
                log.error("委托数量错误，平仓量大于当前持仓量");
                throw new GlobalDefaultException(50017);
            }
        }
        // 委托订单
        EntrustOrder entrustOrder = new EntrustOrder();
        entrustOrder.setUserId(userId)                              // 用户ID
                .setMarketId(market.getId())                        // 交易对ID
                .setMarketType(market.getType())                    // 交易对类型
                .setSymbol(order.getSymbol())                       // 交易对标识符
                .setMarketName(market.getName())                    // 交易市场名称
                .setPrice(price)                                    // 委托价格
                .setVolume(order.getVolume())                       // 委托数量
                .setAmount(BigDecimal.ZERO)                         // 预计成交额(创新交易不需要成交额)
                .setFeeRate(feeRate)                                // 交易手续费费率
                .setFee(orderFee)                                   // 交易手续费
                .setContractUnit(market.getContractUnit())          // 合约单位
                .setDeal(BigDecimal.ZERO)                           // 已成交量
                .setFreeze(orderFee)                                // 冻结资金（手续费）
                .setMarginRate(marginRate)                          // 保证金比例
                .setBaseCoinRate(baseCoinRate)                      // 基础货币对（USDT/BTC）兑换率
                .setPriceCoinRate(priceCoinRate)                    // 报价货币对（USDT/BTC）兑换率
                .setLockMargin(margin)                              // 占用保证金
                .setPriceType(order.getPriceType())                 // 价格类型
                .setTradeType(tradeType.getCode())                  // 交易类型
                .setType(order.getType())                           // 买卖类型
                .setOpenOrderId(order.getOrderId())                 // 平仓关联开仓订单号
                .setStatus(OrderStatus.PENDING.getCode());          // 成交状态
        baseMapper.insert(entrustOrder);
        // 由于委托价更新，因此需要更新交易对信息
        TradeArea tradeArea = tradeAreaService.selectById(market.getTradeAreaId());
        try {
            // 冻结手续费
            accountService.lockAmount(entrustOrder.getUserId(), tradeArea.getCoinId(), entrustOrder.getFee(),
                    BusinessType.FOREX_CREATE_FEE, entrustOrder.getId());
            if (entrustOrder.getTradeType() == TradeType.OPEN_POSITION.getCode()) {
                // 当前委托为开仓：则冻结保证金
                accountService.lockAmount(entrustOrder.getUserId(), tradeArea.getCoinId(), entrustOrder.getLockMargin(),
                        BusinessType.FOREX_CREATE_MARGIN, entrustOrder.getId());
            } else {
                // 当前委托为平仓：则冻结持仓量
                forexAccountService.lockAmount(forexAccount.getId(), entrustOrder.getVolume(),
                        TradeType.CLOSE_POSITION, "平仓冻结");
            }
            // 更新创新交易对行情
            marketService.FOREX_MARKET_CACHE_INSTANCE().refresh(market.getId());
        } catch (AccountException e) {
            log.error("可用保证金不足");
            throw new GlobalDefaultException(50026);
        } catch (ForexAccountException e) {
            log.error("冻结持仓量失败");
            throw new GlobalDefaultException(50017);
        }
        return Response.ok(entrustOrder.getId());
    }

    /**
     * 创新交易撤销委托
     *
     * @param orderId 订单号
     * @param userId  用户ID
     */
    @Override
    @Transactional
    public Market cancelForexOrder(long orderId, long userId) {
        if (orderId <= 0L) {
            log.error("请求参数错误");
            throw new GlobalDefaultException(50008);
        }
        boolean isLocked = false;
        try {
            // 加锁，避免重复提交撤单请求导致重复解冻资金出现资金异常
            isLocked = lockService.getLock(REDIS_KEY_FOREX_ORDER_CANCEL_LOCK, String.valueOf(orderId), false);
            if (!isLocked) {
                log.error("获取撤单锁失败");
                throw new GlobalDefaultException(50010);
            }
            EntrustOrder order = baseMapper.selectById(orderId);
            if (order == null) {
                log.error("撤单失败，委托订单不存在，orderId：{}", orderId);
                throw new GlobalDefaultException(50008);
            }
            if (order.getUserId() != userId) {
                log.error("当前登录用户与委托订单所属用户不一致，订单所属用户：{}，当前登录用户：{}", orderId, userId);
                throw new GlobalDefaultException(50009);
            }
            if (order.getStatus() != OrderStatus.PENDING.getCode()) {
                log.error("撤单失败，orderId：{}, status：{}", orderId, order.getStatus());
                throw new GlobalDefaultException(50010);
            }
            Market market = marketService.selectById(order.getMarketId());
            long tradeAreaId = market.getTradeAreaId();
            TradeAreaDTO tradeArea = tradeAreaService.queryTradeAreaFromCache(tradeAreaId);
            // 更新委托订单状态
            int count = baseMapper.modifyForexEntrustOrder(orderId, BigDecimal.ZERO, BigDecimal.ZERO, OrderStatus.CANCEL.getCode());
            if (count == 1) {
                try {
                    // 解冻手续费
                    accountService.unlockAmount(order.getUserId(), tradeArea.getCoinId(), order.getFreeze(), BusinessType.FOREX_CANCEL_FEE, orderId);
                    if (order.getTradeType() == TradeType.OPEN_POSITION.getCode()) {
                        // 解冻保证金
                        accountService.unlockAmount(order.getUserId(), tradeArea.getCoinId(), order.getLockMargin(), BusinessType.FOREX_CANCEL_MARGIN, orderId);
                    } else {
                        // 查询需要解冻的持仓账户
                        OrderType orderType = OrderType.BUY;
                        if (order.getType() == OrderType.BUY.getCode()) {
                            orderType = OrderType.SELL;
                        }
                        ForexAccount forexAccount = forexAccountService.queryByUserAndMarketAndType(market.getId(), userId, orderType);
                        // 当前委托为平仓：则解冻持仓量
                        forexAccountService.unlockAmount(forexAccount.getId(), order.getVolume(), TradeType.CLOSE_POSITION, "平仓撤单");
                    }
                    // 更新创新交易对行情
                    marketService.FOREX_MARKET_CACHE_INSTANCE().refresh(market.getId());
                    return market;
                } catch (AccountException e) {
                    throw new GlobalDefaultException(50010);
                }
            }
        } catch (GlobalDefaultException e) {
            log.error(e.getMessage());
            e.printStackTrace();
            throw new GlobalDefaultException(50010);
        } finally {
            if (isLocked) {
                lockService.unlock(REDIS_KEY_FOREX_ORDER_CANCEL_LOCK, String.valueOf(orderId));
            }
        }
        log.error("撤单失败，解冻资金账户失败，orderId：{}", orderId);
        throw new GlobalDefaultException(50010);
    }

    /**
     * 查询未完成委托订单
     *
     * @param symbol 交易对标识符
     * @param userId 用户ID
     * @param page   查询条件及分页参数
     * @return
     */
    @Override
    public Page<TradeEntrustOrderDTO> queryEntrustOrder(String symbol, long userId, Page<EntrustOrder> page) {
        EntityWrapper<EntrustOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("user_id", userId)
                .eq("status", OrderStatus.PENDING.getCode());
        if (!"all".equalsIgnoreCase(symbol.trim())) {
            wrapper.eq("symbol", symbol);
        }
        wrapper.orderBy("created", false);
        Page<EntrustOrder> entrustOrderPage = super.selectPage(page, wrapper);
        return this.getTradeEntrustOrderDTOPage(entrustOrderPage, symbol);
    }

    /**
     * 查询历史委托订单
     *
     * @param symbol 交易对标识符
     * @param userId 用户ID
     * @param page   查询条件及分页参数
     * @return
     */
    @Override
    public Page<TradeEntrustOrderDTO> queryHistoryEntrustOrder(String symbol, long userId, Page<EntrustOrder> page) {
        EntityWrapper<EntrustOrder> wrapper = new EntityWrapper<>();
        if (!"all".equalsIgnoreCase(symbol.trim())) {
            wrapper.eq("symbol", symbol);
        }
        wrapper.eq("user_id", userId)
                .andNew("status = {0} or status = {1} or status = {2}", OrderStatus.PENDING.getCode(), OrderStatus.DEAL.getCode(), OrderStatus.CANCEL.getCode())
                .orderBy("created", false);
        Page<EntrustOrder> entrustOrderPage = super.selectPage(page, wrapper);
        return this.getTradeEntrustOrderDTOPage(entrustOrderPage, symbol);
    }

    /**
     * 币币交易市场深度缓存实例
     *
     * @return
     */
    @Override
    public LoadingCache<Long, DepthsDTO> DEPTH_CACHE_INSTANCE() {
        return DEPTH_CACHE_CACHE;
    }

    /**
     * 币币交易市场深度缓存实例
     */
    private LoadingCache<Long, DepthsDTO> DEPTH_CACHE_CACHE = CacheBuilder.newBuilder()
            .build(new CacheLoader<Long, DepthsDTO>() {
                @Override
                public DepthsDTO load(Long marketId) {
                    return initMergeDepth(marketId, DepthMergeType.DEFAULT);
                }
            });

    /**
     * 币币交易市场合并深度缓存实例
     *
     * @return
     */
    @Override
    public LoadingCache<Long, DepthsDTO> MERGE_LOW_DEPTH_CACHE_INSTANCE() {
        return MERGE_LOW_DEPTH_CACHE;
    }

    /**
     * 币币交易市场合并深度缓存实例
     */
    private LoadingCache<Long, DepthsDTO> MERGE_LOW_DEPTH_CACHE = CacheBuilder.newBuilder()
            .build(new CacheLoader<Long, DepthsDTO>() {
                @Override
                public DepthsDTO load(Long marketId) {
                    return initMergeDepth(marketId, DepthMergeType.LOW);
                }
            });

    /**
     * 币币交易市场合并深度缓存实例
     *
     * @return
     */
    @Override
    public LoadingCache<Long, DepthsDTO> MERGE_HIGH_DEPTH_CACHE_INSTANCE() {
        return MERGE_HIGH_DEPTH_CACHE;
    }

    /**
     * 币币交易市场合并深度缓存实例
     */
    private LoadingCache<Long, DepthsDTO> MERGE_HIGH_DEPTH_CACHE = CacheBuilder.newBuilder()
            .build(new CacheLoader<Long, DepthsDTO>() {
                @Override
                public DepthsDTO load(Long marketId) {
                    return initMergeDepth(marketId, DepthMergeType.HIGH);
                }
            });

    /**
     * 初始化深度
     *
     * @return
     */
    private DepthsDTO initMergeDepth(Long marketId, DepthMergeType depthMergeType) {
        DepthsDTO depths = new DepthsDTO();
        // 缓存买单深度
        List<DepthItemDTO> bids = baseMapper.queryBidsMergeDepth(marketId, depthMergeType.getValue());
        // 缓存卖单深度
        List<DepthItemDTO> asks = baseMapper.queryAskMergeDepth(marketId, depthMergeType.getValue());
        depths.setBids(bids).setAsks(asks);
        try {
            BigDecimal price;
            BigDecimal cnyPrice;
            TradeMarketDTO tradeMarket = marketService.TRADE_MARKET_CACHE_INSTANCE().get(marketId);
            if (tradeMarket == null) {
                Market market = marketService.selectById(marketId);
                price = market.getOpenPrice();
                cnyPrice = marketService.getCnyPrice(market).multiply(price).setScale(2, BigDecimal.ROUND_HALF_UP);
            } else {
                price = tradeMarket.getPrice();
                cnyPrice = tradeMarket.getCnyPrice();
            }
            depths.setPrice(price).setCnyPrice(cnyPrice);
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return depths;
    }

    @Override
    public ResultDTO selectDealOrdersTotal(long agencyUserId) {
        List<Map<String, Object>> list = new ArrayList<>();
        for (PriceTradeType type : PriceTradeType.values()) {
            String typeId = type.getCode();
            Map<String, Object> map = baseMapper.selectDealOrderTotal(agencyUserId, typeId);
            if (map != null) {
                list.add(map);
            }
        }
        return ResultDTO.successResult(list);
    }

    /**
     * 获取委托订单
     *
     * @param page       分页
     * @param userName   代理商名称
     * @param orderId    订单ID
     * @param typeId     持仓方向组合ID
     * @param marketId   市场ID
     * @param createTime 时间
     * @return Page
     */
    @Override
    public Page<EntrustOrderDTO> selectOrders(Page<EntrustOrderDTO> page, long agencyUserId, String userName, long orderId, String typeId, long marketId, long parentId, String createTime) {
        List<EntrustOrderDTO> list = new ArrayList<>();

        baseMapper.selectOrders(page, agencyUserId, userName, orderId, typeId, marketId, parentId, createTime).forEach(e -> {
            //获取当前市场最新成交价  计算触发价 取绝对值
            try {
                EntrustOrderDTO entrustOrderDTO = EntrustOrderDTOMapper.INSTANCE.from(e);
                ForexMarketDTO forexMarketDTO = marketService.FOREX_MARKET_CACHE_INSTANCE().get(e.getMarketId());
                entrustOrderDTO.setCurrentPrice(forexMarketDTO.getPrice());
                entrustOrderDTO.setTriggerPrice(new BigDecimal(Math.abs(forexMarketDTO.getPrice().subtract(e.getPrice()).longValue())));
                entrustOrderDTO.setTypeDesc(PriceTradeType.getByCode(e.getType() + "" + e.getTradeType()));
                list.add(entrustOrderDTO);
            } catch (ExecutionException e1) {
                e1.printStackTrace();
            }
        });
        return page.setRecords(list);
    }

    @Override
    public Page<ForexDealOrderDTO> selectDealOrders(Page<ForexDealOrderDTO> page, long agencyUserId, String userName, long orderId, String typeId, long marketId, long parentId, String createTime) {
        List<ForexDealOrderDTO> list = baseMapper.selectDealOrder(page, agencyUserId, userName, orderId, typeId, marketId, parentId, createTime);
        for (int i = 0, l = list.size(); i < l; i++) {
            ForexDealOrderDTO forexDealOrderDTO = list.get(i);
            ForexMarketDTO forexMarketDTO = null;
            try {
                forexMarketDTO = marketService.FOREX_MARKET_CACHE_INSTANCE().get(forexDealOrderDTO.getMarketId());
            } catch (ExecutionException e) {
                e.printStackTrace();
            }

            forexDealOrderDTO.setCurrentPrice(forexMarketDTO.getPrice());
            forexDealOrderDTO.setTypeId(PriceTradeTypes.getByCode(forexDealOrderDTO.getTypeId()));
            list.set(i, forexDealOrderDTO);
        }
        return page.setRecords(list);
    }

    /**
     * 查询创新交易深度
     *
     * @param marketId 交易对ID
     * @return
     */
    @Override
    public List<BigDecimal> selectForexDepth(long marketId) {
        BigDecimal buyPrice = baseMapper.getBuyPrice(marketId, PositionsDirection.BUY.getValue());
        BigDecimal sellPrice = baseMapper.getSellPrice(marketId, PositionsDirection.SELL.getValue());
        List priceList = new ArrayList<>(2);
        priceList.add(buyPrice);
        priceList.add(sellPrice);
        return priceList;
    }

    /**
     * 更新委托单
     *
     * @param orderId    委托订单号
     * @param dealVolume 成交量
     * @param orderType  订单类型
     * @return
     */
    @Override
    public boolean modifyEntrustOrder(Long orderId, BigDecimal dealVolume, OrderType orderType) {
        if (orderType == OrderType.BUY) {
            return baseMapper.modifyEntrustBuyOrder(orderId, dealVolume) > 0;
        }
        return baseMapper.modifyEntrustSellOrder(orderId, dealVolume) > 0;
    }

    @Override
    public BigDecimal selectBuyFirstPrice(Long marketId) {
        return baseMapper.selectBuyFirstPrice(marketId);
    }

    @Override
    public BigDecimal selectSellFirstPrice(Long marketId) {
        return baseMapper.selectSellFirstPrice(marketId);
    }

    /**
     * 将entrustOrder转换为entrustOrderDTO
     *
     * @param entrustOrderPage
     * @param symbol
     * @return
     */
    private Page<TradeEntrustOrderDTO> getTradeEntrustOrderDTOPage(Page<EntrustOrder> entrustOrderPage, String symbol) {
        Page<TradeEntrustOrderDTO> result = new Page<>();
        result.setSize(entrustOrderPage.getSize())
                .setCurrent(entrustOrderPage.getCurrent())
                .setTotal(entrustOrderPage.getTotal());
        if (!CollectionUtils.isEmpty(entrustOrderPage.getRecords())) {
            List<TradeEntrustOrderDTO> entrustOrderDTOList = new ArrayList<>(entrustOrderPage.getRecords().size());
            for (EntrustOrder entrustOrder : entrustOrderPage.getRecords()) {
                symbol = entrustOrder.getSymbol();
                Market market = marketService.queryBySymbol(symbol);
                TradeEntrustOrderDTO entrustOrderDTO = TradeEntrustOrderDTOMapper.INSTANCE.from(entrustOrder);
                entrustOrderDTOList.add(entrustOrderDTO);
                entrustOrderDTO.setOrderId(entrustOrder.getId());
                entrustOrderDTO.setDealVolume(entrustOrder.getDeal());
                entrustOrderDTO.setSymbol(entrustOrder.getSymbol());
                EntityWrapper<TurnoverOrder> turnoverOrderWrapper = new EntityWrapper<>();
                turnoverOrderWrapper.eq("symbol", symbol)
                        .eq("status", OrderStatus.DEAL.getCode());
                if (entrustOrder.getType() == OrderType.BUY.getCode()) {
                    turnoverOrderWrapper.eq("buy_order_id", entrustOrder.getId());
                } else {
                    turnoverOrderWrapper.eq("sell_order_id", entrustOrder.getId());
                }
                List<TurnoverOrder> turnoverOrderList = turnoverOrderService.selectList(turnoverOrderWrapper);
                BigDecimal dealAmount = BigDecimal.ZERO;
                for (TurnoverOrder turnoverOrder : turnoverOrderList) {
                    dealAmount = dealAmount.add(turnoverOrder.getAmount());
                }
                entrustOrderDTO.setDealAvgPrice(BigDecimal.ZERO);
                if (entrustOrder.getDeal().compareTo(BigDecimal.ZERO) > 0) {
                    BigDecimal dealAvgPrice = dealAmount.divide(entrustOrder.getDeal(), market.getPriceScale(), BigDecimal.ROUND_HALF_UP);
                    entrustOrderDTO.setDealAvgPrice(dealAvgPrice);
                }
                entrustOrderDTO.setDealAmount(dealAmount);
            }
            result.setRecords(entrustOrderDTOList);
        }
        return result;
    }

    /**
     * 计算合并深度价格
     *
     * @param price      原始价格
     * @param mergeDepth 合并规则
     * @param mergeType  合并类型
     * @return
     */
    private BigDecimal getMergePrice(BigDecimal price, String mergeDepth, DepthMergeType mergeType) {
        if (StringUtils.isEmpty(mergeDepth)) {
            return price;
        }
        String[] mergeDepthArray = mergeDepth.split(",");
        int scale = Integer.parseInt(mergeDepthArray[mergeType.getValue()]);
        return price.setScale(scale, BigDecimal.ROUND_DOWN);
    }

    /**
     * 今日委托
     *
     * @param userId 用户ID
     * @return
     */
    @Override
    public List<ForexEntrustOrderDTO> selectTodayList(long userId) {
        List<Long> marketIds = new ArrayList<>();
        marketService.queryByType(MarketType.FOREX).forEach(m -> marketIds.add(m.getId()));
        EntityWrapper<ForexEntrustOrderDTO> wrapper = new EntityWrapper<>();
        wrapper.eq("user_id", userId)
                .in("market_id", marketIds);
        List<ForexEntrustOrderDTO> forexEntrustOrders = baseMapper.selectTodayList(wrapper);
        if (CollectionUtils.isEmpty(forexEntrustOrders)) {
            return new ArrayList<>();
        }
        for (ForexEntrustOrderDTO entrustOrder : forexEntrustOrders) {
            ForexMarketDTO forexMarket = marketService.getMarketDataById(entrustOrder.getMarketId());
            Market market = marketService.getMarketById(entrustOrder.getMarketId());
            // 委托买卖类型为买：（买一价 - 委托价） / 点；
            // 委托买卖类型为卖：（委托价 - 卖一价） / 点；
            BigDecimal priceValue;
            if (entrustOrder.getType() == PositionsDirection.BUY.getValue()) {
                priceValue = forexMarket.getBidPrice().subtract(entrustOrder.getPrice());
            } else {
                priceValue = entrustOrder.getPrice().subtract(forexMarket.getAskPrice());
            }
            entrustOrder.setPriceRange(priceValue.divide(market.getPointValue(), 8, BigDecimal.ROUND_HALF_UP));
            entrustOrder.setDealAvgPrice(entrustOrder.getDealAvgPrice().setScale(market.getPriceScale(), BigDecimal.ROUND_HALF_UP));
        }
        return forexEntrustOrders;
    }

    /**
     * 获取持仓方向下拉框值
     *
     * @return
     */
    @Override
    public List<Map<String, Object>> getEntrustType() {
        List<Map<String, Object>> list = new ArrayList<>();
        for (PositionsDirection p : PositionsDirection.values()) {
            for (TradeType t : TradeType.values()) {
                Map<String, Object> map = new HashMap<>();
                String typeId = String.valueOf(p.getValue()) + t.getCode();
                String typeName = p.getDesc() + t.getName();
                map.put("typeId", typeId);
                map.put("typeName", typeName);
                list.add(map);
            }
        }
        return list;
    }
}
